package directory

import (
	"errors"
	"fmt"
	"gitee.com/zackeus/go-boot/freeswitch/mod/xmlcurl"
	"gitee.com/zackeus/goutil/strutil"
	"github.com/beevik/etree"
	"strconv"
)

type (
	Option func(xml *Xml)

	Xml struct {
		doc       *etree.Document
		variables *etree.Element
		agentNo   string
		agentName string
	}
)

const (
	dialStringFormat = "{presence_id=%s@%s,presence_agent_no=%s,presence_agent_name=%s,accountcode=%s}${sofia_contact(${dialed_user}@${domain_name})}"
)

func New(user string, password string, domain string, userCtx string, opts ...Option) (xmlcurl.XmlConf, error) {
	xml := &Xml{
		doc: etree.NewDocument(),
	}
	/* 属性值不转译 */
	xml.doc.WriteSettings.CanonicalAttrVal = true

	document := xml.doc.CreateElement("document")
	document.CreateAttr("type", "freeswitch/xml")

	section := document.CreateElement("section")
	section.CreateAttr("name", "directory")

	domainElement := section.CreateElement("domain")
	domainElement.CreateAttr("name", domain)

	variables := domainElement.CreateElement("variables")
	variable1 := variables.CreateElement("variable")
	variable1.CreateAttr("name", "language")
	variable1.CreateAttr("value", "zh_CN")
	variable2 := variables.CreateElement("variable")
	variable2.CreateAttr("name", "default_language")
	variable2.CreateAttr("value", "zh_CN")

	groups := domainElement.CreateElement("groups")
	group := groups.CreateElement("group")
	group.CreateAttr("name", "default")

	users := group.CreateElement("users")

	userElement := users.CreateElement("user")
	userElement.CreateAttr("id", user)
	userParams := userElement.CreateElement("params")
	userParam := userParams.CreateElement("param")
	/* 密码 hash  md5 user:domain:password */
	userParam.CreateAttr("name", "a1-hash")
	userParam.CreateAttr("value", strutil.Md5(fmt.Sprintf("%s:%s:%s", user, domain, password)))

	userVariables := userElement.CreateElement("variables")
	xml.variables = userVariables

	xml.createVariable("presence_id", fmt.Sprintf("%s@%s", user, domain))
	xml.createVariable("accountcode", user)
	/* 绑定路由 */
	xml.createVariable("user_context", userCtx)
	xml.createVariable("toll_allow", "domestic,international,local")
	/* 来电显示 */
	xml.createVariable("effective_caller_id_name", user)
	xml.createVariable("effective_caller_id_number", user)
	/* originate 来电显示 */
	xml.createVariable("outbound_caller_id_name", user)
	xml.createVariable("outbound_caller_id_number", user)
	xml.createVariable("callgroup", "techsupport")
	/* 呼叫超时 */
	xml.createVariable("call_timeout", "30")

	for _, opt := range opts {
		opt(xml)
	}

	params := domainElement.CreateElement("params")
	param := params.CreateElement("param")
	param.CreateAttr("name", "dial-string")
	/* 绑定坐席 */
	param.CreateAttr("value", fmt.Sprintf(dialStringFormat, user, domain, xml.agentNo, xml.agentName, user))
	return xml, nil
}

func (x *Xml) createVariable(name string, value string) {
	variable := x.variables.FindElement(fmt.Sprintf("./variable[@name='%s']", name))
	if variable == nil {
		variable = x.variables.CreateElement("variable")
	}
	variable.CreateAttr("name", name)
	variable.CreateAttr("value", value)
}

// WithCallTimeout 呼叫超时
func WithCallTimeout(n uint64) Option {
	return func(xml *Xml) {
		xml.createVariable("call_timeout", strconv.FormatUint(n, 10))
	}
}

// WithEffectiveCallerIdName 来电显示名称
func WithEffectiveCallerIdName(s string) Option {
	return func(xml *Xml) {
		xml.createVariable("effective_caller_id_name", s)
	}
}

// WithEffectiveCallerIdNumber 来电显示号码
func WithEffectiveCallerIdNumber(s string) Option {
	return func(xml *Xml) {
		xml.createVariable("effective_caller_id_number", s)
	}
}

// WithAgentNo 绑定的坐席工号
func WithAgentNo(s string) Option {
	return func(xml *Xml) {
		xml.agentNo = s
		xml.createVariable("presence_agent_no", s)
	}
}

// WithAgentName 绑定的坐席名称
func WithAgentName(s string) Option {
	return func(xml *Xml) {
		xml.agentName = s
		xml.createVariable("presence_agent_name", s)
	}
}

func (x *Xml) BodyElement() *etree.Element {
	return x.variables
}

func (x *Xml) MarshalToXml() ([]byte, error) {
	x.doc.Indent(1)
	return x.doc.WriteToBytes()
}

func (x *Xml) UnMarshalFromString(v string) error {
	if x.doc == nil {
		x.doc = etree.NewDocument()
		/* 属性值不转译 */
		x.doc.WriteSettings.CanonicalAttrVal = true
	}
	if err := x.doc.ReadFromString(v); err != nil {
		return err
	}

	variables := x.doc.FindElement("./document/section/domain/groups/group/users/user/variables")
	if variables == nil {
		return errors.New("the element document/section/domain/groups/group/users/user/variables is nil")
	}
	x.variables = variables

	agentNo := x.variables.FindElement(fmt.Sprintf("./variable[@name=presence_agent_no]"))
	if agentNo != nil {
		x.agentNo = agentNo.SelectAttrValue("value", "")
	}
	agentName := x.variables.FindElement(fmt.Sprintf("./variable[@name=presence_agent_name]"))
	if agentName != nil {
		x.agentName = agentName.SelectAttrValue("value", "")
	}
	return nil
}

func (x *Xml) WriteToFile(path string) error {
	x.doc.Indent(1)
	return x.doc.WriteToFile(path)
}
